﻿namespace Blaze.Framework.Loaders
{
    using System;
    using System.Collections;
    using Logging;

    /// <summary>
    /// 用于批量加载任意资源。
    /// </summary>
    public sealed class AssetBatchLoader : AssetLoaderBase
    {
        /// <summary>
        /// 获取加载进度。
        /// </summary>
        public override float Progress
        {
            get { return mProgress; }
        }

        /// <summary>
        /// 获取批量加载的结果，顺序与加载请求一致。
        /// </summary>
        public object[] Results { get; private set; }

        /// <summary>
        /// 创建一个<see cref="AssetBatchLoader"/>。
        /// </summary>
        /// <param name="subUris">需要批量加载的资源Uri</param>
        public static AssetBatchLoader GetFromPool(string[] subUris)
        {
            var uri = $"batch_{mCounter++}";
            var ret = AssetLoaderPool.Get<AssetBatchLoader>(uri);
            ret.mSubUris = subUris;
            ret.mSubLoaders = new AssetLoader[subUris.Length];
            ret.Results = new object[subUris.Length];
            return ret;
        }

        /// <summary>
        /// 当加载器需要被释放时调用此方法。
        /// </summary>
        protected override void OnDispose()
        {
            for (var i = 0; i < mSubLoaders.Length; i++)
                AssetLoaderPool.Put(mSubLoaders[i]);
            mSubLoaders = null;
            mSubUris = null;
            Results = null;
            base.OnDispose();
        }

        /// <summary>
        /// 当加载器启动时调用此方法。
        /// </summary>
        protected override void OnStart()
        {
            for (var i = 0; i < mSubUris.Length; i++)
            {
                var subUri = mSubUris[i];
                var subLoader = AssetLoaderPool.Get<AssetLoader>(subUri);
                mSubLoaders[i] = subLoader;
                subLoader.Mode = Mode;
                subLoader.Start();
            }

            switch (Mode)
            {
                case LoadAssetMode.Sync:
                    for (int i = 0; i < mSubLoaders.Length; i++)
                    {
                        var subLoader = mSubLoaders[i];
                        if (!subLoader.IsOk)
                        {
                            Finish(subLoader.Error, null);
                            return;
                        }
                        Results[i] = subLoader.Result;
                    }
                    Finish(null, Results);
                    break;
                case LoadAssetMode.Async:
                    CoroutineManager.StartNew(run());
                    break;
                default:
                    throw new NotSupportedException();
            }
        }

        private IEnumerator run()
        {
            while (true)
            {
                var finished = 0;
                mProgress = 0;
                for (var i = 0; i < mSubLoaders.Length; i++)
                {
                    var subLoader = mSubLoaders[i];
                    if (!subLoader.IsDone)
                        continue;

                    if (subLoader.IsOk)
                    {
                        mProgress += subLoader.Progress;
                        finished++;
                    }
                    else
                    {
                        Finish(subLoader.Error, null);
                        yield break;
                    }
                }
                mProgress /= mSubLoaders.Length;
                if (finished == mSubLoaders.Length)
                    break;
                yield return null;
            }
            for (var i = 0; i < mSubLoaders.Length; i++)
                Results[i] = mSubLoaders[i].Result;
            Finish(null, Results);
        }

        private static int mCounter = 1;
        private static readonly Log mLog = LogManager.GetLogger(typeof(AssetBatchLoader));
        private float mProgress;
        private AssetLoader[] mSubLoaders;
        private string[] mSubUris;
    }
}